package com.csthink.auditlog.enums;

import com.csthink.auditlog.vo.CategoryVO;
import com.csthink.common.infrastructure.util.I18nUtil;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author <a href="mailto:security.2009@live.cn">Mars</a>
 * @since 2023-09-12
 */
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
@JsonPropertyOrder({"key", "name"})
public enum CategoryEnum {

    UNKNOWN("Unknown", Scope.ALL, ""),

    OP_USER("OPUser", Scope.OP,"alg.category.OPUser"),
    OP_ROLE("OPRole", Scope.OP,"alg.category.OPRole"),
    OP_MENU("OPMenu", Scope.OP,"alg.category.OPMenu"),
    OP_CACHE("OPCache", Scope.OP,"alg.category.OPCache"),
    OP_ACCOUNT_OPTIONS("AccountOptions", Scope.OP, "alg.category.AccountOptions"),
    OP_CORS("CORS domain whitelist", Scope.OP, "alg.category.whitelist" ),


    PREFERENCES("Preferences", Scope.WEB, "alg.category.preferences"),
    API_KEY("API Key", Scope.WEB, "alg.category.api.key"),
    ACCOUNT_SETTINGS("Account Settings", Scope.WEB, "alg.category.account.settings"),
    SYSTEM_CONFIG("System Config", Scope.OP, "alg.category.system.config"),

    ;

    private final String name;
    private final int scope;
    @Getter
    private final String i18nKey;

    CategoryEnum(String name, int scope, String i18nKey) {
        this.name = name;
        this.scope = scope;
        this.i18nKey = i18nKey;
    }


    public String getKey() {
        return name();
    }

    public String getName() {
        return null == name ? name() : name;
    }

    public int getScope() {
        return scope;
    }

    public static CategoryEnum parse(String name) {
        for (CategoryEnum category : values()) {
            if (category.getName().equals(name)) {
                return category;
            }
        }
        return null;
    }

    /**
     * Filter categories through scope
     * Scope value is in Category.Scope, you can use bit-op-or(|) to get more
     * than one scope.
     *
     * @param scope
     * @return Categories in the given scope.
     */
    public static CategoryEnum[] values(int scope) {
        return Stream.of(values())
                .filter(c -> (c.scope & scope) > 0)
                .toArray(CategoryEnum[]::new);
    }

    public static CategoryEnum[] getValues() {
        return Stream.of(values())
                .filter(c -> (!StringUtils.equalsIgnoreCase(c.name, CategoryEnum.UNKNOWN.name())
                        && c.scope == Scope.WEB))
                .toArray(CategoryEnum[]::new);
    }

    public static List<CategoryVO> getI18nValues() {
        return Stream.of(values())
                .filter(c -> (!StringUtils.equalsIgnoreCase(c.name, CategoryEnum.UNKNOWN.name())
                        && c.scope == Scope.WEB))
                .map(c -> CategoryVO.builder()
                        .name(I18nUtil.getMessage(c.getI18nKey()))
                        .key(c.getName())
                        .scope(c.getScope())
                        .build())
                .collect(Collectors.toList());
    }

    /**
     * Use bit set to mark category's scope
     * You can use OP|WEB to represent that, contains in both OP and WEB
     */
    public static final class Scope {
        // None scope
        public static final int NONE = 0x00000000;
        // All scope
        public static final int ALL = 0xffffffff;
        // The lowest bit represents that contains in OP
        public static final int OP = 0x00000001;
        // The 2nd lowest bit represents that contains in WEB
        public static final int WEB = 0x00000002;
        // The 3rd lowest bit represents that contains in API
        public static final int API = 0x00000004;
    }
}
